package neptune.console;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import neptune.console.cli.commands.Init;
import neptune.manager.NeptuneManager;
import neptune.manager.allocator.MainAllocator;
import neptune.manager.builder.IPAddressRange;
import neptune.manager.configurator.SSHConfigurator;
import neptune.manager.model.topology.IPAddress;
import neptune.manager.storage.ExperimentsRepository;
import neptune.manager.storage.VmTemplateRepository;
import neptune.manager.storage.XMLApplicationPropertiesStorageFactory;
import neptune.manager.storage.XMLClusterStorageFactory;
import neptune.manager.storage.XMLExperimentsRepository;
import neptune.manager.storage.XMLTemplateStorageFactory;
import neptune.manager.storage.XMLTopologyStorageFactory;
import neptune.manager.storage.XMLVmTemplateRepository;

import org.apache.log4j.BasicConfigurator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 
 */

/**
 * @author oltremago
 * 
 */
public class NeptuneCLI {

	private static String version = "0.5.0";
	private static NeptuneManager neptune;
	private static XMLConfigurationRepository configRepository;
	private @Autowired
	List<CLCommand> commands;

	// private static final Logger log = Logger.getLogger(NeptuneCLI.class);

	public static NeptuneManager getNeptune() {
		return neptune;
	}

	public static XMLConfigurationRepository getConf() {
		return configRepository;
	}

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		System.out.println("Neptune Command Line Interface - v. " + version);

		String confPath = System.getProperty("user.home") + File.separator
				+ ".neptune";

		BasicConfigurator.configure();

		if (args.length > 0) {
			if (args[0].charAt(0) != '-')
				confPath = args[0];
			else if (args[0].substring(1).equals("init")) {
				Init init = new Init();
				init.processCommand(null);
				System.out
						.println("<userhome>/.neptune configuration directory created");
				System.exit(0);
			}
		}

		System.out.println("Configuration location: " + confPath);

		ApplicationContext ctx = new ClassPathXmlApplicationContext(
				"neptune-cli-beans.xml");
		NeptuneCLI cli = (NeptuneCLI) ctx.getBean("cli");
		try {
			cli.initialize(confPath);
		} catch (RuntimeException e) {
			System.out
					.println("Unable to initialize Neptune, have you run init command?");
		}

		cli.processArgs(args);

		System.out.println("All done, thanks for using Neptune - goodbye!");

	}

	public void initialize(String confPath) {
		configRepository = new XMLConfigurationRepository(confPath);

		ExperimentsRepository expRepo = new XMLExperimentsRepository(
				configRepository.getExperimentsDirPath());

		XMLTopologyStorageFactory topologyStorageFactory = new XMLTopologyStorageFactory(
				configRepository.getTopologyRootDirectoryPath());

		File expDir = new File(configRepository.getExperimentsDirPath());
		expDir.mkdirs();
		File topolDir = new File(configRepository
				.getTopologyRootDirectoryPath());
		topolDir.mkdirs();

		// TODO trovare un modo di definire a runtime allocator e configurator.

		IPAddressRange controlRange = configRepository.getControlIPRange();
		IPAddress baseExperimentSubnet = configRepository
				.getBaseExperimentSubnet();
		IPAddress clusterSubnet = configRepository.getClusterSubnet();
		String linkSubnet = configRepository.getLinkSubnet();

		VmTemplateRepository vmTemplateRepository = new XMLVmTemplateRepository(
				configRepository.getVmTempRepDir());

		neptune = new NeptuneManager(expRepo, topologyStorageFactory,
				new XMLClusterStorageFactory(configRepository
						.getClusterFilePath()), new XMLTemplateStorageFactory(
						configRepository.getTemplatePath()),
				new XMLApplicationPropertiesStorageFactory(configRepository
						.getConfigurationFilePath()), new MainAllocator(
						configRepository.getApplicationRootDirectoryPath(),
						configRepository.getVmTempRepDir(), configRepository
								.getExperimentsVMsDir()), new SSHConfigurator(
						controlRange), controlRange, baseExperimentSubnet,
				clusterSubnet, linkSubnet, vmTemplateRepository);

	}

	public void processArgs(String[] args) throws Exception {

		if (args.length == 0)
			System.out
					.println("Usage: NeptuneCLI [<conf_file>] [-<command> [<command_params>]]\n"
							+ "Use -h to show help");

		List<String> argsList = new ArrayList<String>(args.length);
		argsList.addAll(Arrays.asList(args));

		for (int i = 0; i < args.length; i++) {
			if (args[i].charAt(0) == '-') {
				argsList.remove(0);
				processCommand(args[i].substring(1), argsList);
			}
		}

	}

	private void processCommand(String cmdName, List<String> argsList)
			throws Exception {

		assert commands.size() > 0;

		if (cmdName.equals("h") || cmdName.equals("help")) {
			printHelp();
			return;
		}

		boolean found = false;
		for (CLCommand cmd : commands) {
			if (cmd.getCommandName().equals(cmdName)) {
				cmd.processCommand(argsList);
				found = true;
			}
		}

		if (!found)
			throw new Exception("Command '" + cmdName + "' is undefined!");
	}

	private void printHelp() {
		System.out
				.println("Usage: NeptuneCLI [<conf_file>] [-<command> [<command_params>]]\n"
						+ "Use -h to show this help");
		System.out.println("\tCommands:\n");

		for (CLCommand cmd : commands) {
			System.out.println("\t" + cmd.getCommandName() + " \t-\t"
					+ cmd.getCommandHelp() + "; Params: "
					+ cmd.getParamsDescription());
		}
	}
}
